#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "esp_log.h"

#define SPI_EXAM_HOST HSPI_HOST
#define PIN_NUM_MISO 22
#define PIN_NUM_MOSI 19
#define PIN_NUM_CLK  23
#define PIN_NUM_CS   18
#define PIN_NUM_RST  25

const char* TAG = "SPI_EXAMPLE";
spi_device_handle_t spi_handle;

typedef struct {
    uint8_t addr;
    uint8_t data;
    uint8_t hook; //No of data in data; bit 0 = flush READY; bit 1 = check after write; 0xFF = end of cmds.
} cmd_t;
const cmd_t spi_init_cmds[]={
    {0x07, 0b00000000, 0x1},
    {0x08, 0b00000000, 0x1},
    {0x09, 0b00000000, 0x1},
    {0x0A, 0b01010101, 0x1},
    {0x0B, 0b10101100, 0x1},
    {0x0C, 0b11000000, 0x1},
    {0x0D, 0b00000000, 0x1},
    {0x0E, 0b01110000, 0x1},
    {0x0F, 0b00000000, 0x3},
    {0, 0, 0xff}
};

void Spi_write(uint8_t address, uint8_t data)
{
    esp_err_t ret;
    spi_transaction_t t;

    memset(&t, 0, sizeof(t));
    t.length=8;
    t.cmd=2;
    t.addr=address;
    t.flags = SPI_TRANS_USE_TXDATA;
    t.tx_data[0]=data;
    ret=spi_device_polling_transmit(spi_handle, &t);
    assert(ret==ESP_OK);
}

uint32_t Spi_read(uint8_t address)
{
    esp_err_t ret;
    spi_transaction_t t;
    memset(&t, 0, sizeof(t));
    t.cmd=1;
    t.addr=address;
    t.length=8;
    t.rxlength=8;
    t.flags = SPI_TRANS_USE_RXDATA|SPI_TRANS_USE_TXDATA;
    ret=spi_device_polling_transmit(spi_handle, &t);
    ESP_ERROR_CHECK(ret);
    return t.rx_data[0];
}

void Spi_init()
{
    esp_err_t ret;
    spi_bus_config_t buscfg={
        .miso_io_num=PIN_NUM_MISO,
        .mosi_io_num=PIN_NUM_MOSI,
        .sclk_io_num=PIN_NUM_CLK,
        .quadwp_io_num=-1,
        .quadhd_io_num=-1
    };
    spi_device_interface_config_t devcfg={
        .clock_speed_hz=1000,
        .mode=0,
        .spics_io_num=PIN_NUM_CS,
        .queue_size=1,
        .command_bits=2,
        .address_bits=6,
    };
    ret=spi_bus_initialize(SPI_EXAM_HOST, &buscfg, 0);
    ESP_ERROR_CHECK(ret);
    ret=spi_bus_add_device(SPI_EXAM_HOST, &devcfg, &spi_handle);
    ESP_ERROR_CHECK(ret);
    ESP_LOGI(TAG, "SPI Successfully Enabled");
}

int Spi_rst()
{
    uint16_t cmd=0;
    gpio_set_level(PIN_NUM_RST, 1);
    vTaskDelay(10 / portTICK_RATE_MS);
    gpio_set_level(PIN_NUM_RST, 0);
    vTaskDelay(10 / portTICK_RATE_MS);
    gpio_set_level(PIN_NUM_RST, 1);
    vTaskDelay(10 / portTICK_RATE_MS);
    while (spi_init_cmds[cmd].hook!=0xff) {
        Spi_write(spi_init_cmds[cmd].addr, spi_init_cmds[cmd].data);
        cmd++;
    }
    ESP_LOGI(TAG, "Init Successfully Finished");
    cmd = 0;
    while (spi_init_cmds[cmd].hook!=0xff) {
        if(spi_init_cmds[cmd].hook&0x1){
            uint8_t t = (uint8_t)Spi_read(spi_init_cmds[cmd].addr);
            if(t == spi_init_cmds[cmd].data){
                ESP_LOGI(TAG, "Successful in checking addr: 0x%x, init Data: 0x%x, reg: 0x%x", spi_init_cmds[cmd].addr, spi_init_cmds[cmd].data, t);
            } else {
                ESP_LOGW(TAG, "Failed in checking addr: 0x%x, init Data: 0x%x, reg: 0x%x", spi_init_cmds[cmd].addr, spi_init_cmds[cmd].data, t);
            }
        }
        cmd++;
    }
    return 0;
}

void app_main(void)
{
    Spi_init();
    Spi_rst();
}
